package com.qqt.csr.common.log;

import cn.hutool.core.util.IdUtil;
import com.google.common.base.Stopwatch;
import com.qqt.csr.common.session.SessionContextHolder;
import com.qqt.csr.common.utils.JsonUtil;
import com.qqt.csr.common.vo.req.RequestHeader;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;

/**
 * 用于打印请求参数和返回结果日志
 *
 * @auther:
 * @Date: 2024-05-16 15:18
 */
@Component
@Aspect
@Order(-1)
@Slf4j
public class RequestLogAspect {
    private static final String LOG_TID = "ctid";
    private volatile boolean isLoadServletRequestClass = isLoadClass("jakarta.servlet.ServletRequest");

    @Pointcut("@annotation(com.qqt.csr.common.log.Log)")
    public void logAspect() {
    }

    @Around("logAspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        MDC.put(LOG_TID, IdUtil.getSnowflake().nextIdStr());

        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Log logAnnotation = method.getAnnotation(Log.class);

        StringBuilder logDesc = new StringBuilder();
        logDesc.append(joinPoint.getSignature().getDeclaringTypeName())
                .append(".")
                .append(joinPoint.getSignature().getName())
                .append(",")
                .append("desc: ")
                .append(logAnnotation.desc())
                .append(",");

        if (logAnnotation.outContext()) {
            RequestHeader header = SessionContextHolder.getHeaderInfo();
            logDesc.append("header:").append(JsonUtil.toJson(header)).append(",");
        }

        if (logAnnotation.outReqParam()) {
            logDesc.append("request: ").append(getRequestParam(method.getParameters(), joinPoint.getArgs()));
        }

        Stopwatch stopwatch = Stopwatch.createStarted();
        try {
            Object result = joinPoint.proceed();
            if (logAnnotation.outRespParam()) {
                logDesc.append("response: ").append(JsonUtil.toJson(result)).append(",");
            }
            return result;
        } finally {
            stopwatch.stop();
            logDesc.append("rt: ").append(stopwatch.elapsed(TimeUnit.MILLISECONDS)).append("ms");
            log.info("{}", logDesc);
            MDC.clear();
        }
    }

    /**
     * 拼接请求参数
     *
     * @param paramsArgsName
     * @param paramsArgsValue
     * @return
     */
    private String getRequestParam(Parameter[] paramsArgsName, Object[] paramsArgsValue) {
        if (ArrayUtils.isEmpty(paramsArgsName) || ArrayUtils.isEmpty(paramsArgsValue)) {
            return "";
        }

        try {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < paramsArgsValue.length; i++) {
                // 参数名
                String name = paramsArgsName[i].getName();
                // 参数值
                Object value = paramsArgsValue[i];
                sb.append(name).append("=");
                if (value == null) {
                    sb.append("null").append(",");
                } else if (value instanceof String) {
                    sb.append(value).append(",");
                } else if (value instanceof Byte[] || value instanceof byte[]) {
                    sb.append("字节数组参数不打印！");
                }

                if(isLoadServletRequestClass){
                    if (value instanceof ServletRequest request) {
                        sb.append(JsonUtil.toJson(request.getParameterMap()));
                    } else if (value instanceof ServletResponse || value instanceof MultipartFile) {
                        sb.append("特殊字段不打印！");
                    } else {
                        sb.append(JsonUtil.toJson(value)).append(",");
                    }
                }else{
                    sb.append(JsonUtil.toJson(value)).append(",");
                }
            }
            return sb.toString();
        } catch (Exception e) {
            log.error("打印请求参数失败", e);
            return "获取请求参数失败,";
        }
    }

    private boolean isLoadClass(String className) {
        try {
            Class.forName(className);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
}
